// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraPerformanceStatSubsystem.h" // 包含头文件

#include "Engine/Engine.h" // 包含引擎头文件
#include "Engine/GameInstance.h" // 包含游戏实例头文件
#include "Engine/NetConnection.h" // 包含网络连接头文件
#include "Engine/World.h" // 包含世界头文件
#include "GameFramework/PlayerState.h" // 包含玩家状态头文件
#include "GameModes/LyraGameState.h" // 包含Lyra游戏状态头文件
#include "Performance/LyraPerformanceStatTypes.h" // 包含Lyra性能统计类型
#include "Performance/LatencyMarkerModule.h" // 包含延迟标记模块头文件
#include "ProfilingDebugging/CsvProfiler.h" // 包含CSV分析器头文件

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraPerformanceStatSubsystem) // 包含由UHT生成的代码

CSV_DEFINE_CATEGORY(LyraPerformance, /*bIsEnabledByDefault=*/false); // 定义CSV类别 "LyraPerformance"，默认禁用

class FSubsystemCollectionBase; // 前置声明：子系统集合基类

//////////////////////////////////////////////////////////////////////
// FLyraPerformanceStatCache

/**
 * 开始图表记录。当前为空实现。
 */
void FLyraPerformanceStatCache::StartCharting()
{
}

/**
 * 处理一帧的性能数据，记录各种统计信息。
 * @param FrameData 包含帧性能数据的结构体。
 */
void FLyraPerformanceStatCache::ProcessFrame(const FFrameData& FrameData)
{
	// 记录关于帧数据的统计信息
	{
		// 记录客户端FPS：如果真实增量时间不为零，则计算FPS（1/DeltaTime），否则记录0
		RecordStat(
			ELyraDisplayablePerformanceStat::ClientFPS,
			(FrameData.TrueDeltaSeconds != 0.0) ?
			1.0 / FrameData.TrueDeltaSeconds :
			0.0);

		RecordStat(ELyraDisplayablePerformanceStat::IdleTime, FrameData.IdleSeconds); // 记录空闲时间
		RecordStat(ELyraDisplayablePerformanceStat::FrameTime, FrameData.TrueDeltaSeconds); // 记录整体帧时间
		RecordStat(ELyraDisplayablePerformanceStat::FrameTime_GameThread, FrameData.GameThreadTimeSeconds); // 记录游戏线程时间
		RecordStat(ELyraDisplayablePerformanceStat::FrameTime_RenderThread, FrameData.RenderThreadTimeSeconds); // 记录渲染线程时间
		RecordStat(ELyraDisplayablePerformanceStat::FrameTime_RHIThread, FrameData.RHIThreadTimeSeconds); // 记录RHI线程时间
		RecordStat(ELyraDisplayablePerformanceStat::FrameTime_GPU, FrameData.GPUTimeSeconds); // 记录GPU时间
	}

	if (UWorld* World = MySubsystem->GetGameInstance()->GetWorld()) // 获取世界对象
	{
		// 记录一些网络相关的统计信息
		if (const ALyraGameState* GameState = World->GetGameState<ALyraGameState>()) // 获取Lyra游戏状态
		{
			RecordStat(ELyraDisplayablePerformanceStat::ServerFPS, GameState->GetServerFPS()); // 记录服务器FPS
		}

		if (APlayerController* LocalPC = GEngine->GetFirstLocalPlayerController(World)) // 获取第一个本地玩家控制器
		{
			if (APlayerState* PS = LocalPC->GetPlayerState<APlayerState>()) // 获取玩家状态
			{
				RecordStat(ELyraDisplayablePerformanceStat::Ping, PS->GetPingInMilliseconds()); // 记录网络延迟（Ping）
			}

			if (UNetConnection* NetConnection = LocalPC->GetNetConnection()) // 获取网络连接
			{
				const UNetConnection::FNetConnectionPacketLoss& InLoss = NetConnection->GetInLossPercentage(); // 获取传入丢包率数据
				RecordStat(ELyraDisplayablePerformanceStat::PacketLoss_Incoming, InLoss.GetAvgLossPercentage()); // 记录平均传入丢包率
				
				const UNetConnection::FNetConnectionPacketLoss& OutLoss = NetConnection->GetOutLossPercentage(); // 获取传出丢包率数据
				RecordStat(ELyraDisplayablePerformanceStat::PacketLoss_Outgoing, OutLoss.GetAvgLossPercentage()); // 记录平均传出丢包率
				
				RecordStat(ELyraDisplayablePerformanceStat::PacketRate_Incoming, NetConnection->InPacketsPerSecond); // 记录传入数据包速率
				RecordStat(ELyraDisplayablePerformanceStat::PacketRate_Outgoing, NetConnection->OutPacketsPerSecond); // 记录传出数据包速率

				// 记录传入数据包平均大小：如果传入数据包速率不为零，则计算平均大小（传入字节数/传入数据包数），否则记录0
				RecordStat(ELyraDisplayablePerformanceStat::PacketSize_Incoming, (NetConnection->InPacketsPerSecond != 0) ? (NetConnection->InBytesPerSecond / (float)NetConnection->InPacketsPerSecond) : 0.0f);
				// 记录传出数据包平均大小：如果传出数据包速率不为零，则计算平均大小（传出字节数/传出数据包数），否则记录0
				RecordStat(ELyraDisplayablePerformanceStat::PacketSize_Outgoing, (NetConnection->OutPacketsPerSecond != 0) ? (NetConnection->OutBytesPerSecond / (float)NetConnection->OutPacketsPerSecond) : 0.0f);
			}
			
			// 最后，如果启用了输入延迟相关的统计信息，则记录它们
			TArray<ILatencyMarkerModule*> LatencyMarkerModules = IModularFeatures::Get().GetModularFeatureImplementations<ILatencyMarkerModule>(ILatencyMarkerModule::GetModularFeatureName()); // 获取所有延迟标记模块实现
			for (ILatencyMarkerModule* LatencyMarkerModule : LatencyMarkerModules) // 遍历每个延迟标记模块
			{
				if (LatencyMarkerModule->GetEnabled()) // 检查模块是否启用
				{
					const float TotalLatencyMs = LatencyMarkerModule->GetTotalLatencyInMs(); // 获取总延迟（毫秒）
					if (TotalLatencyMs > 0.0f) // 如果总延迟大于0
					{
						// 记录一些关于游戏延迟的统计信息
						RecordStat(ELyraDisplayablePerformanceStat::Latency_Total, TotalLatencyMs); // 记录总延迟
						RecordStat(ELyraDisplayablePerformanceStat::Latency_Game, LatencyMarkerModule->GetGameLatencyInMs()); // 记录游戏延迟
						RecordStat(ELyraDisplayablePerformanceStat::Latency_Render, LatencyMarkerModule->GetRenderLatencyInMs()); // 记录渲染延迟

						// 记录一些CSV性能分析统计信息。
						// 您可以使用以下命令查看这些信息：
						// 启动和停止性能分析：
						//	CsvProfile Start
						//	CsvProfile Stop
						//
						// 或者，您可以分析特定数量的帧：
						// CsvProfile Frames=10
						//
						// 这将输出一个.csv文件到 Saved\Profiling\CSV 文件夹
#if CSV_PROFILER // 如果启用了CSV分析器
						if (FCsvProfiler* Profiler = FCsvProfiler::Get()) // 获取CSV分析器实例
						{
							static const FName TotalLatencyStatName = TEXT("Lyra_Latency_Total"); // 定义总延迟统计名称
							Profiler->RecordCustomStat(TotalLatencyStatName, CSV_CATEGORY_INDEX(LyraPerformance), TotalLatencyMs, ECsvCustomStatOp::Set); // 记录总延迟自定义统计

							static const FName GameLatencyStatName = TEXT("Lyra_Latency_Game"); // 定义游戏延迟统计名称
							Profiler->RecordCustomStat(GameLatencyStatName, CSV_CATEGORY_INDEX(LyraPerformance), LatencyMarkerModule->GetGameLatencyInMs(), ECsvCustomStatOp::Set); // 记录游戏延迟自定义统计

							static const FName RenderLatencyStatName = TEXT("Lyra_Latency_Render"); // 定义渲染延迟统计名称
							Profiler->RecordCustomStat(RenderLatencyStatName, CSV_CATEGORY_INDEX(LyraPerformance), LatencyMarkerModule->GetRenderLatencyInMs(), ECsvCustomStatOp::Set); // 记录渲染延迟自定义统计
						}
#endif // CSV_PROFILER 结束

						// 如果需要，可以在标记模块上找到更细粒度的延迟数字
						//LatencyMarkerModule->GetRenderLatencyInMs()));
						//LatencyMarkerModule->GetDriverLatencyInMs()));
						//LatencyMarkerModule->GetOSRenderQueueLatencyInMs()));
						//LatencyMarkerModule->GetGPURenderLatencyInMs()));
						break; // 找到一个启用的模块后跳出循环
					}
				}
			}
		}
	}
}

/**
 * 停止图表记录。当前为空实现。
 */
void FLyraPerformanceStatCache::StopCharting()
{
}

/**
 * 记录一个性能统计值到缓存中。
 * @param Stat 要记录的统计指标。
 * @param Value 统计值。
 */
void FLyraPerformanceStatCache::RecordStat(const ELyraDisplayablePerformanceStat Stat, const double Value)
{
	PerfStateCache.FindOrAdd(Stat).RecordSample(Value); // 在缓存映射中查找或添加对应统计指标的FSampledStatCache，并记录样本值
}

/**
 * 获取指定性能统计指标的缓存值。
 * @param Stat 要查询的性能统计指标。
 * @return 该统计指标的缓存值，如果未找到则返回0.0。
 */
double FLyraPerformanceStatCache::GetCachedStat(ELyraDisplayablePerformanceStat Stat) const
{
	static_assert((int32)ELyraDisplayablePerformanceStat::Count == 18, "需要更新此函数以处理新的性能统计信息"); // 静态断言，确保统计指标数量为18，否则需要更新此函数

	if (const FSampledStatCache* Cache = GetCachedStatData(Stat)) // 获取缓存数据指针
	{
		return Cache->GetLastCachedStat(); // 返回最后缓存的统计值
	}

	return 0.0; // 如果未找到缓存，返回0.0
}

/**
 * 获取指定性能统计指标的缓存数据。
 * @param Stat 要查询的性能统计指标。
 * @return 指向该统计指标缓存数据的常量指针，如果未找到则返回 nullptr。
 */
const FSampledStatCache* FLyraPerformanceStatCache::GetCachedStatData(const ELyraDisplayablePerformanceStat Stat) const
{
	static_assert((int32)ELyraDisplayablePerformanceStat::Count == 18, "需要更新此函数以处理新的性能统计信息"); // 静态断言，确保统计指标数量为18，否则需要更新此函数
	
	return PerfStateCache.Find(Stat); // 在缓存映射中查找并返回对应统计指标的FSampledStatCache指针
}

//////////////////////////////////////////////////////////////////////
// ULyraPerformanceStatSubsystem

/**
 * 初始化性能统计子系统。
 * 创建性能统计跟踪器并将其添加到引擎的性能数据消费者列表中。
 * @param Collection 子系统集合。
 */
void ULyraPerformanceStatSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Tracker = MakeShared<FLyraPerformanceStatCache>(this); // 创建FLyraPerformanceStatCache共享指针，传入this指针
	GEngine->AddPerformanceDataConsumer(Tracker); // 将跟踪器添加到引擎的性能数据消费者列表中
}

/**
 * 反初始化性能统计子系统。
 * 从引擎的性能数据消费者列表中移除跟踪器并重置它。
 */
void ULyraPerformanceStatSubsystem::Deinitialize()
{
	GEngine->RemovePerformanceDataConsumer(Tracker); // 从引擎的性能数据消费者列表中移除跟踪器
	Tracker.Reset(); // 重置跟踪器共享指针
}

/**
 * 获取指定性能统计指标的缓存值。
 * @param Stat 要查询的性能统计指标。
 * @return 该统计指标的缓存值。
 */
double ULyraPerformanceStatSubsystem::GetCachedStat(ELyraDisplayablePerformanceStat Stat) const
{
	return Tracker->GetCachedStat(Stat); // 委托给跟踪器的GetCachedStat方法
}

/**
 * 获取指定性能统计指标的缓存数据。
 * @param Stat 要查询的性能统计指标。
 * @return 指向该统计指标缓存数据的常量指针。
 */
const FSampledStatCache* ULyraPerformanceStatSubsystem::GetCachedStatData(const ELyraDisplayablePerformanceStat Stat) const
{
	return Tracker->GetCachedStatData(Stat); // 委托给跟踪器的GetCachedStatData方法
}